#include "TemperatureStuff.h"
#include "Serial.h"
#include "1Wire.h"
#include "Config.h"
#include "MyCan.h"
#include "ChargeControl.h"
#include "userConfig.h"
#include "CanMessage.h"
#include "leds.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>

#define READ_ROM_COMMAND  	0x33         // Command to read the 64-bit serial number from 1-wire slave device.
#define SKIP_ROM_COMMAND        0xcc
#define MATCH_ROM_COMMAND       0x55
#define SEARCH_ROM_COMMAND      0xf0

#define INVALID_TEMPERATURE INT_MIN
#define MISSING_SENSOR INT_MIN + 1


#define TIMEOUTS_BEFORE_ABANDON_EXTERNAL 20
#define TIMEOUTS_FOR_READING_TEMPERATURE 4

unsigned char serial_numbers[MAX_DEVICE_COUNT][8];
int DevicesConnected = 0;
int TemperatureReadings[MAX_DEVICE_COUNT];

unsigned char TemperatureMessageData[8];


int ExternalDeviceCount;
ExternalSensorPacket ExternalData[MAX_EXTERNAL_DEVICES];

int TimerForReadingTemperatures;

void hex_ascii(unsigned char);
int getTemperature(int index);
void requestConversion();
void waitForConversionComplete();
void ResetExternalData(ExternalSensorPacket* data);


void InitializeTemperatureStuff()
{
    int deviceCount, byteIndex;
    for(deviceCount = 0; deviceCount < MAX_DEVICE_COUNT; deviceCount++)
    {
        for(byteIndex = 0; byteIndex < 8; byteIndex++)
        {
            serial_numbers[deviceCount][byteIndex] = 0;
        }
        TemperatureReadings[deviceCount] = INVALID_TEMPERATURE;
    }

    ExternalSensorPacket *data;
    for(deviceCount = 0; deviceCount < MAX_EXTERNAL_DEVICES; deviceCount++)
    {
        data = ExternalData + deviceCount;
        data->SA = 0xff;
        ResetExternalData(data);
        data->age = 0;
    }
    TimerForReadingTemperatures = 0;
}

void ResetExternalData(ExternalSensorPacket* data)
{
    int byteIndex;
    for(byteIndex = 0; byteIndex < 7; byteIndex++)
    {
        data->tempValues[byteIndex] = 0xfe;
    }
}


void PrintAllSerialNumbers()
{
    int deviceCount, byteIndex;
    for(deviceCount = 0; deviceCount < MAX_DEVICE_COUNT; deviceCount++)
    {
        PutString("Device ");
        Putchar('0' + deviceCount);
        Putchar(' ');
        for(byteIndex = 0; byteIndex < 8; byteIndex++)
        {
            hex_ascii(serial_numbers[deviceCount][byteIndex]);
        }
        Putchar('\r');
    }

}

void ScanForSerialNumbers()
{
    int deviceCount;
    int temp;
    if(OWFirst())
    {
        for(deviceCount = 0; deviceCount < MAX_DEVICE_COUNT; deviceCount++)
        {
            for(temp = 0; temp < 8; temp++)
            {
                serial_numbers[deviceCount][temp] = ROM_NO[temp];
            }
            if(!OWNext())
            {
                break;
            }

        }

        PutString("I found ");
        Putchar('0' + deviceCount);
        PutString("devices\r");
        DevicesConnected = deviceCount + 1;
    }
    else
    {
        PutString("No Devices Found");
    }
}

void requestConversion(void)
{
   OW_reset_pulse();

   OW_write_byte(SKIP_ROM_COMMAND); //match rom
   OW_write_byte(0x44); //convert temp
}

void waitForConversionComplete()
{
    while(OW_read_bit() == 0); //converting temp
}

void GetAllTemperatures()
{
    int i;
    int localTemperatureList[MAX_DEVICE_COUNT];
    requestConversion();
    waitForConversionComplete();
    for (i = 0; i < DevicesConnected; i++)
    {
       int temperature = getTemperature(i);
       localTemperatureList[i] = temperature;
    }
    for(; i < GetInternalNodeChargeCount(); i++)
    {
        localTemperatureList[i] = MISSING_SENSOR;
    }
    for(; i < MAX_DEVICE_COUNT; i++)
    {
        localTemperatureList[i] = INVALID_TEMPERATURE;
    }


    __builtin_disi(0x3FFF);
    memcpy(TemperatureReadings, localTemperatureList, sizeof(localTemperatureList));
    __builtin_disi(0);
}

void PrintAllTemperatures()
{
    int i;
    for(i = 0; i < MAX_DEVICE_COUNT; i++)
    {
       PutString("IS ");
       Putchar('0' + i);
       PutString(" is ");
       char buff[10];
       itoa(buff, TemperatureReadings[i], 10);
       PutString(buff);
       Putchar('\r');
    }
    for(i = 0; i < MAX_EXTERNAL_DEVICES; i++)
    {
        PutString("EM ");
        Putchar('0' + i);
        PutString(" SA:");
        hex_ascii(ExternalData[i].SA);
        PutString(" Data: ");
        char buff[10];
        int j;
        for(j = 0; j < 7; j++)
        {
            itoa(buff, ExternalData[i].tempValues[j], 10);
            PutString(buff);
            Putchar(' ');
        }

        PutString("Age: ");
        itoa(buff, ExternalData[i].age, 10);
        PutString(buff);
       Putchar('\r');
    }
}

int getTemperature(int index)
{
    int temp;
    unsigned char padData[9];

   OW_reset_pulse();
   OW_write_byte(MATCH_ROM_COMMAND); //match rom
   for(temp = 0; temp < 8; temp++)
   {
       OW_write_byte(serial_numbers[index][temp]);
   }
   OW_write_byte(0xbe); //read scratchpad

   crc8 = 0;
   for(temp = 0; temp<9; temp++)
   {
   		padData[temp] = OW_read_byte();	// Read 64-bit registration (48-bit serial number) number from 1-wire Slave Device
        docrc8(padData[temp]);
   }

   if(crc8 != 0)
   {
       return MISSING_SENSOR;
   }
   else
   {
        return ( ((char)padData[1] << 4) + (padData[0] >> 4) );
   }
}

void ChangeTo9BitConversion()
{
   OW_reset_pulse();

   OW_write_byte(SKIP_ROM_COMMAND); //match rom
   OW_write_byte(0x4E); //Write Scratchpad
   OW_write_byte(0);
   OW_write_byte(0);
   OW_write_byte(0x1F);
   OW_reset_pulse();
}

unsigned char ConvertCelciusToCAN(int temperature)
{
    unsigned char retVal = 0xff;
    if(temperature != INVALID_TEMPERATURE)
    {
        if(temperature == MISSING_SENSOR)
        {
            retVal = 0xfe;
        }
        else
        {
        retVal = temperature + 100;
        }
    }
    return retVal;
}

void SendTemperatureMessage()
{
    TemperatureMessageData[0] = ChargeState;
    int temp;
    for(temp = 0; temp < MAX_DEVICE_COUNT; temp++)
    {
        TemperatureMessageData[temp+1] = ConvertCelciusToCAN(TemperatureReadings[temp]);
    }
    SendCANMessage(StitchSAIntoFullIdentifier(TEMPERATURE_STATUS_MESSAGE_BASE, GetCANAddress()), TemperatureMessageData, 1);
    LED_SetGroup(ChargeState);
}

void HandlerExternalTemperatureMessagePriv(unsigned char SA, unsigned char* data)
{
    int dataIndex;
    for(dataIndex = 0; dataIndex < ExternalDeviceCount; dataIndex++)
    {
        if(ExternalData[dataIndex].SA == SA)
        {
            break;
        }
    }

    if(dataIndex == ExternalDeviceCount)
    {
        ExternalDeviceCount++;
    }

    ExternalData[dataIndex].SA = SA;
    memcpy(ExternalData[dataIndex].tempValues, data + 1, 7);
    ExternalData[dataIndex].age = 0;
}


void TS_HandleExternalTemperatureMessage(int SID, int EID, unsigned char* data)
{
    unsigned char SA = ExtractSAFromEID(EID);
    HandlerExternalTemperatureMessagePriv(SA, data);
}

void TS_HandleExternalTemperatureMessageNew(CANMessage* message)
{
    unsigned char SA = message->ID & 0xff;
    HandlerExternalTemperatureMessagePriv(SA, message->Data);
}


void TS_HandleTimerTick()
{
    TimerForReadingTemperatures++;
    int dataIndex;
    ExternalSensorPacket* data;
    for(dataIndex = 0; dataIndex < ExternalDeviceCount; dataIndex++)
    {
        data = ExternalData + dataIndex;
        if(data->SA != 0xFF)
        {
            data->age++;
            if(data->age > TIMEOUTS_BEFORE_ABANDON_EXTERNAL)
            {
                ResetExternalData(data);
                data->age = TIMEOUTS_BEFORE_ABANDON_EXTERNAL;
            }
        }
    }
}

void TS_ProcessMainLoop()
{
    static int previousInternalButtonValue = 1;
    static int previousExternalButtonValue = 1;

    int shouldRun = 0;

    __builtin_disi(0x3FFF);
    if(TimerForReadingTemperatures > TIMEOUTS_FOR_READING_TEMPERATURE)
    {
        shouldRun = 1;
        TimerForReadingTemperatures = 0;
    }
    __builtin_disi(0);

    if(shouldRun)
    {
       GetAllTemperatures();
//       PrintAllTemperatures();
//       Putchar('\r');
//       Putchar('\r');
       SendTemperatureMessage();
       LED_ToggleStatus();

       if(!PORTBbits.RB0 && !previousInternalButtonValue)
       {
           PutString("Syncing Node Count\r");
           SetInternalNodeChargeCount(DevicesConnected);
       }
       previousInternalButtonValue = PORTBbits.RB0;

       if(!PORTBbits.RB1 && !previousExternalButtonValue)
       {
           PutString("Syncing External Node Count\r");
           SetExternalNodeChargeCount(ExternalDeviceCount);
       }
       previousExternalButtonValue = PORTBbits.RB1;

    }
}